package net.kehuilai;

import com.alibaba.fastjson.JSON;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import net.kehuilai.handler.ClientHandler;
import net.kehuilai.handler.MessageDecoderHandler;
import net.kehuilai.handler.MessageEncoderHandler;
import net.kehuilai.model.BaseMessage;
import net.kehuilai.util.StringTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by genghz on 16/12/6.
 */
public class SocketClient {

    private static final Logger log = LoggerFactory.getLogger(SocketClient.class);

    private final ClientHandler handler;

    private final String host;
    private final int port;

    private NioEventLoopGroup group;
    private Channel channel;

    public SocketClient(String host, int port) {
        this.host = host;
        this.port = port;
        this.handler = new ClientHandler();
    }

    public void start() {
        this.group = new NioEventLoopGroup();
        final SocketClient client = this;
        try {
            Bootstrap boot = new Bootstrap();
            boot.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            // 设置拦截处理栈
                            channel.pipeline()
                                    .addLast("packer", new LengthFieldPrepender(4))   // 自动设置消息长度
                                    .addLast("unpacker", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))   // 自动过滤消息长度
                                    .addLast("encoder", new MessageEncoderHandler())
                                    .addLast("decoder", new MessageDecoderHandler())
                                    .addLast("handler", handler);
                        }
                    });
            ChannelFuture future = boot.connect(host, port);
            future.syncUninterruptibly();
            this.channel = future.channel();
        } catch (Exception e) {
            this.close();
        }
    }

    public void close() {
        channel.close();
        group.shutdownGracefully();
    }

    public void send(String method, Object msg) {
        if (channel == null) {
            log.error("client connect failed {}:{}", this.host, this.port);
            return;
        }
        BaseMessage message = new BaseMessage(method, msg.getClass().getName(), JSON.toJSONString(msg));
        this.channel.writeAndFlush(message);
    }

    public Object sendSync(String method, Object msg) {
        if (channel == null) {
            log.error("client connect failed {}:{}", this.host, this.port);
            return null;
        }

        String msgId = StringTool.md5(System.currentTimeMillis() + "-" + StringTool.getRandomStr(4));
        if (StringTool.isEmpty(msgId)) {
            log.error("[send_sync] create req_id failed");
            return null;
        }
        BaseMessage message = new BaseMessage(msgId, method, msg.getClass().getName(), JSON.toJSONString(msg));
        this.channel.writeAndFlush(message);
        return handler.getResponse(msgId);
    }
}
